# model.py
"""
Lib-Ray specific structures and object model representing releases, volumes, etc.
"""

import sys, os, re, shutil, datetime
from ConfigParser import ConfigParser, NoOptionError
from StringIO import StringIO

# Player-global settings, such as installation directories
# are read by config.Configuration
if __name__=='__main__':
    import config
    config.set_unittest_context()
from config import Configuration
Config = Configuration()

class Session(object):
    """
    The 'session' holds the current global state of the software.
    
    >>> session = Session(mountpoint=os.path.join(Config.source, 'test', 'lrtest', 'volumes', '0.4'))
    >>> session.volume.valid
    True
    >>> session.volume.name
    'ASW-4'
    >>>
    >>> session.archive.delete_by_name(session.volume.name)
    False
    >>> session.archive.from_media(session.volume)
    >>> session.archive.delete_by_name(session.volume.name)
    True
    >>>
    
    """
    def __init__(self, mountpoint=None):
        if not mountpoint:
            print "This alpha version of the Lib-Ray player needs to be given a"
            print "directory path pointing to an already-mounted Lib-Ray volume."
            mountpoint = raw_input("Volume directory? ")
        self.volume = Volume(os.path.abspath(mountpoint))
        self.archive = Archive()


class Archive(object):
    """
    The "archive" is the collection of movies stored/cached on the player at the current time. Playback can be either from an inserted medium, or from one of
    the movies in the archive.
    """
    def __init__(self):
        self.path = Config.archive_path
        self.titles = {}
        items = os.listdir(self.path)
        for item in items:
            vol = Volume(os.path.join(self.path, item))
            if vol.valid:
                self.titles[vol.name] = (vol.path, vol.cover, vol.description)
            
    def delete_by_name(self, name):
        try:
            title = self.titles[name]
        except KeyError:
            return False
        try:
            shutil.rmtree(title.path)
            del self.titles[name]
            return True
        except:
            print "Unable to delete volume (Is it a symlink? Do you have write permission on your archive?)."
            return False
        
    def from_media(self, vol):
        try:
            archived_path = os.path.join(self.path, vol.name)
            shutil.copytree(vol.path, archived_path, symlinks=True)
            arc = Volume(archived_path)
            self.titles[arc.id] = (arc.path, arc.cover, arc.description)
        except:
            e, s, t = sys.exc_info()
            print "Error copying volume: %s" % e
            print "Value: %s" % s
            print "Traceback:\n%s" % str(t)
            
       

class Volume(object):
    """
    The "volume" is an individual Lib-Ray movie on disk, flash-media card, or stored in the archive.
    
    >>> testvol = Volume('test/lrtest/volumes/0.4')
    >>> testvol.name
    'ASW-4'
    """
    def __init__(self, volume_path):
        self.path = volume_path
        self.log = ""
        self.valid = (self._load_meta() & self._find_media_files())
    
    def _load_meta(self):
        metapath = os.path.join(self.path, 'meta.cnf')
        if not os.path.exists(metapath):
            return False
        parser = ConfigParser()
        parser.read(metapath)
        
        try:
            # The following fields are mandatory for Lib-Ray compatibility
            self.version = tuple([int(i) for i in parser.get('Lib-Ray', 'Version').split('.')][:2])
            self.producer = parser.get('Lib-Ray', 'Producer')
            self.release = parser.get('Lib-Ray', 'Release')
            try:
              s = parser.get('Lib-Ray', 'Date')
              ymd = [int(i) for i in re.split(r'[-/]', s)]
              self.date = datetime.date(*ymd)
            except:
              self.log += "\nDate unparseable.\n"
              return False
        except NoOptionError:
            self.log += "\nRequired field missing from meta.cnf.\n"
            return False
            
        try:
            self.title = parser.get('Lib-Ray', 'Title')
        except NoOptionError:
            self.log += "\nNo title found in meta.cnf.\n"
            self.title = 'Untitled'
            
        try:
            self.description = parser.get('Lib-Ray', 'Description')
        except NoOptionError:
            self.log += "\nNo description found in meta.cnf. Using title.\n"
            self.description = self.title
            
        try:
            self.cover = os.path.join(self.path, parser.get('Lib-Ray', 'cover'))
        except NoOptionError:
            self.log += "\nNo cover picture path found in meta.cnf. Using default.\n"
            self.cover = 'cover.png'
            if not os.path.exists(os.path.join(self.path, self.cover)):
                self.log += "\nCover image not found. Invalid Lib-Ray volume.\n"
                return False
            
        self.name = '-'.join((self.producer, self.release))
        return True
        
    def _find_media_files(self):
        mediapath = os.path.join(self.path, 'Media')
        if not os.path.exists(mediapath):
            return False
        self.mediafiles = [f for f in os.listdir(mediapath) if f[-4:] in ('.lrv', '.mkv')]
        if len(self.mediafiles) < 1:
            return False
        else:
            return True
        
class MenuPage(object):
    """
    A single HTML document in the Lib-Ray menu system.
    """
    pass
  
  
class VideoFile(object):
    """
    A single LRV video file in a Lib-Ray volume.
    """
    pass
        

class enum(object):
    def __init__(self, items, doc="Enumerated Type with Values"):
        self.table = items
        self.__doc__ = doc
        for key in items:
            setattr(self, key, key)
    def __repr__(self):
        rep = "%s:\n" % self.__doc__
        for val in self.table:
            rep += ("\t%s\t%s\n" % (val, self.table[val]))
        return rep
           
    
# Special Accessibility menu modes:
SpecialAccessibility = {
    'SKIPMENU':         'Play feature video immediately, as for children',
    'MUTEMENU':         'Turn off sound in menus',
    'COLORBLIND':       'Use colorblind-safe color scheme in menus',
    }
SA = enum(SpecialAccessibility,"Special Accessibility modes (change behavior of menus):")

# Region Codes:
RegionCode = {
    'TE':       "Global / Earth",
    'NA':       "North America",
    'SA':       "South America",
    'EU':       "Europe",
    'AF':       "Africa",
    'WA':       "West Asia",
    'EA':       "East Asia",
    'PA':       "Pacific",
    }
RC = enum(RegionCode, "Lib-Ray regions (to control language and locale menus):")

      
# Standard doctest test runner
if __name__ == "__main__":
    print "Running doctest unittests."
    import doctest
    doctest.testmod()
    print "Tests complete."
  
